#include<stdio.h>
#include<string.h>
#include<math.h>
#include<time.h>
#include<ctype.h>
#include<omp.h>
#include<iostream>
#include<list>

using namespace std;

#define TAM_MAX_PALAVRA 100
#define TAM_MAX_FRASE 200
#define BILLION 1E9

/* Retorna 1 se a string é um palindromo, se não retorna 0 */
int palindromo(char string[]){
	int i, tam, tam_aux, flag = 1;
	char string_aux[TAM_MAX_PALAVRA];

	strcpy(string_aux, string);

	tam = strlen(string_aux);
	tam_aux = tam/2;

	for(i = 0; i < tam; i++)
		if(isupper(string_aux[i]))
			string_aux[i] = tolower(string_aux[i]);

	for(i = 0; i < tam_aux; i++){
		if(string_aux[i] != string_aux[tam-i-1])
			flag = 0;
	}

	return flag;
}

int main(int argc, char *argv[]){
	char c, palavra[TAM_MAX_PALAVRA], frase[TAM_MAX_FRASE];
	int i = 0, flag = 0, total_palin_pal = 0, total_palin_fra = 0, threads;
	FILE *fp, *fp_out;
	struct timespec time1, time2, time3, time4;
	double diff, diff2;

	list<string> palavras;
	list<string> frases;
	list<string>::iterator it;
	string aux;

	sscanf(argv[1], "%d", &threads);

	fp = fopen("shakespe.txt", "rb+");
	fp_out = fopen("shakespe_out.txt", "wb+");

	/* Pre-processamento do arquivo, apenas queremos letras e numeros, o resto vira _, end of line vira * */
	c = fgetc(fp);
	while(c != EOF){
		/* Se for diferente de numeros ou letras, colocamos _, se for EOL colocamos * */
		if (c == '\n')
			fputc('*', fp_out);
		else if(!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z'))
			fputc('_', fp_out);
		else
			fputc(c, fp_out);
		c = fgetc(fp);
	}

	/* Agora voltamos para o inicio do arquivo e separamos as palavras */
	fseek(fp_out, 0, SEEK_SET);
	c = fgetc(fp_out);
	it = palavras.begin();
	while(c != EOF){
		if(c != '_' && c != '*') /* nossos delimitadores são _ e * */
			palavra[i++] = c;
		else if (i != 0)
			flag = 1;

		if(flag == 1){
			palavra[i] = '\0';
			if(i > 1)
				palavras.insert(it, palavra);
			i = 0;
			flag = 0;
		}
		c = fgetc(fp_out);
	}

	/* Voltamos para o inicio do arquivo e separamos as frases */
	fseek(fp_out, 0, SEEK_SET);
	c = fgetc(fp_out);
	it = frases.begin();
	while(c != EOF){
		if(c != '_' && c != '*') /* nosso delimitador é *, _ é ignorado */
			frase[i++] = c;
		else if (c == '*' && i != 0)
			flag = 1;

		if(flag == 1){
			frase[i] = '\0';
			if(i > 1)
				frases.insert(it, frase);
			i = 0;
			flag = 0;
		}
		c = fgetc(fp_out);
	}

	clock_gettime(CLOCK_REALTIME, &time1);
	#pragma omp parallel \
	num_threads(threads) private(it, i, palavra, aux) shared(palavras) reduction(+:total_palin_pal)
	{
		/* Percorre a lista de palavras procurando palindromos */
		for(it=palavras.begin(); it != palavras.end(); it++){
			#pragma omp single nowait
			{
				aux = *it;

				/* Passamos a palavra de string (c++) para um array de chars (c) */
				for(i = 0; i < aux.size(); i++)
					palavra[i] = aux[i];
				palavra[i] = '\0';

				if(palindromo(palavra))
					total_palin_pal++;
			}
		}
	}
	clock_gettime(CLOCK_REALTIME, &time2);

	clock_gettime(CLOCK_REALTIME, &time3);
	#pragma omp parallel \
	num_threads(threads) private(it, i, frase, aux) shared(frases) reduction(+:total_palin_fra)
	{
		/* Percorre a lista de frases procurando palindromos */
		for(it=frases.begin(); it != frases.end(); it++){
			#pragma omp single nowait
			{
				aux = *it;

				/* Passamos a frase de string (c++) para um array de chars (c) */
				for(i = 0; i < aux.size(); i++)
					frase[i] = aux[i];
				frase[i] = '\0';

				if(palindromo(frase))
					total_palin_fra++;
			}
		}
	}
	clock_gettime(CLOCK_REALTIME, &time4);

	diff = (time2.tv_sec-time1.tv_sec)+(time2.tv_nsec-time1.tv_nsec)/BILLION;
	diff2 = (time4.tv_sec-time3.tv_sec)+(time4.tv_nsec-time3.tv_nsec)/BILLION;

	printf("Numero de palindromos no texto (palavras) : %d\n", total_palin_pal);
	printf("Tempo para verificação de palindromos (palavras): %lf segundos\n",diff);
	printf("Numero de palindromos no texto (frases) : %d\n", total_palin_fra);
	printf("Tempo para verificação de palindromos (frases): %lf segundos\n",diff2);

	fclose(fp);
	fclose(fp_out);

	return 0;
}
